package org.geektimes.rest.client;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.geektimes.rest.core.DefaultResponse;

import javax.ws.rs.HttpMethod;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.InvocationCallback;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Future;

/**
 * @FileName: HttpPostInvocation
 * @Description: TODO
 * @Author: MaWenHao
 * @Created: 2021/3/31 - 下午11:35
 */
public class HttpPostInvocation implements Invocation {

    private final URI uri;

    private final URL url;

    private final MultivaluedMap<String, Object> headers;
    
    private final Entity<?> entity;

    HttpPostInvocation(URI uri, MultivaluedMap<String, Object> headers, Entity<?> entity) {
        this.uri = uri;
        this.headers = headers;
        this.entity = entity;
        try {
            this.url = uri.toURL();
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException();
        }
    }
    
    @Override
    public Invocation property(String name, Object value) {
        return this;
    }

    @Override
    public Response invoke() {
        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod(HttpMethod.POST);
            // http正文内，因此需要设为true, 默认情况下是false;
            connection.setDoOutput(true);
            // Post 请求不能使用缓存
            connection.setUseCaches(false);
            setRequestHeaders(connection);
            setMediaType(connection);
            setRequestBody(connection);

            int responseCode = connection.getResponseCode();
            DefaultResponse response = new DefaultResponse();
            response.setStatus(responseCode);
            response.setConnection(connection);
            return response;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void setMediaType(HttpURLConnection connection) {
        if (Objects.nonNull(entity) && Objects.nonNull(entity.getMediaType())) {
            String mediaType = entity.getMediaType().getType() + "/" + entity.getMediaType().getSubtype();
            connection.setRequestProperty("Content-Type", mediaType);
        }
    }

    private void setRequestBody(HttpURLConnection connection) {
        if (Objects.nonNull(entity) && Objects.nonNull(entity.getEntity())) {
            try(OutputStream outputStream = connection.getOutputStream()) {
                ObjectMapper objectMapper = new ObjectMapper();
                String content = objectMapper.writeValueAsString(entity.getEntity());
                byte[] bytes = null;
                if (Objects.nonNull(entity.getEncoding())) {
                    bytes = content.getBytes(entity.getEncoding());
                } else {
                    bytes = content.getBytes();
                }
                outputStream.write(bytes);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void setRequestHeaders(HttpURLConnection connection) {
        Set<Map.Entry<String, List<Object>>> entries = this.headers.entrySet();
        for (Map.Entry<String, List<Object>> entry : entries) {
            String headerName = entry.getKey();
            for (Object value : entry.getValue()) {
                connection.setRequestProperty(headerName, String.valueOf(value));
            }
        }
    }

    @Override
    public <T> T invoke(Class<T> responseType) {
        return null;
    }

    @Override
    public <T> T invoke(GenericType<T> responseType) {
        return null;
    }

    @Override
    public Future<Response> submit() {
        return null;
    }

    @Override
    public <T> Future<T> submit(Class<T> responseType) {
        return null;
    }

    @Override
    public <T> Future<T> submit(GenericType<T> responseType) {
        return null;
    }

    @Override
    public <T> Future<T> submit(InvocationCallback<T> callback) {
        return null;
    }
}
